package wdsys

import (
	"encoding/json"
	"expvar"
	"html/template"
	"net/http"
	"runtime"
	"strconv"
	"strings"
	"time"

	"gitee.com/yutiandou/wdtools/wdstruct"
)

type Graphics struct {
	Kind  string
	ID    string
	Name  string
	Lable string
	Key   []string
	Value []string
}
type ExpNode struct {
	time string
	ms   *runtime.MemStats
}

var Graphicslist = make([]Graphics, 0)
var expvarinfo = wdstruct.New_Double_Link()

//getExpvarInfo 获取程序运行信息
func getExpvarInfo() {
	data := &ExpNode{
		time: time.Now().Format("04:05"),
	}
	expvar.Do(func(kv expvar.KeyValue) {
		if kv.Key == "memstats" {
			obj := &runtime.MemStats{}
			json.Unmarshal([]byte(kv.Value.String()), obj)
			data.ms = obj
		}
	})
	expvarinfo.AddToTail(data)
	if expvarinfo.GetLen() > 10 {
		expvarinfo.DeleteFromHead()
	}
}

//monitoringhandle url响应函数
func monitoringhandle(w http.ResponseWriter, r *http.Request) {
	_, file, _, ok := runtime.Caller(0)
	if !ok {
		w.Write([]byte("template loading fail"))
		return
	}
	file = strings.Trim(file, "system_monitoring.go")
	temp, err := template.ParseFiles(file + "monitor.html")
	if err != nil {
		w.Write([]byte("template loading fail"))
		w.Write([]byte(err.Error()))
		return
	}
	head := expvarinfo.GetHead()
	for i, _ := range Graphicslist {
		// if v == nil {
		// 	break
		// }
		Graphicslist[i].Key = make([]string, 0)
		Graphicslist[i].Value = make([]string, 0)
		total, available, used, percent := GetMemoryInfo()
		Graphicslist[0].Key = []string{"total", "available", "used", "percent"}
		Graphicslist[0].Value = make([]string, 4)
		Graphicslist[0].Value[0] = strconv.Itoa(int(total / 1000000))
		Graphicslist[0].Value[1] = strconv.Itoa(int(available / 1000000))
		Graphicslist[0].Value[2] = strconv.Itoa(int(used / 1000000))
		Graphicslist[0].Value[3] = strconv.Itoa(int(percent * 100))
		for {
			if head.IsNull() {
				break
			}
			data := head.GetData().(*ExpNode)
			switch Graphicslist[i].Kind {
			case "line":
				Graphicslist[i].Key = append(Graphicslist[i].Key, data.time)
				switch Graphicslist[i].ID {
				case "Alloc":
					Graphicslist[i].Value = append(Graphicslist[i].Value, strconv.Itoa(int(data.ms.Alloc/1000)))
				case "TotalAlloc":
					Graphicslist[i].Value = append(Graphicslist[i].Value, strconv.Itoa(int(data.ms.TotalAlloc/1000)))
				case "Sys":
					Graphicslist[i].Value = append(Graphicslist[i].Value, strconv.Itoa(int(data.ms.Sys/1000)))
				case "Lookups":
					Graphicslist[i].Value = append(Graphicslist[i].Value, strconv.Itoa(int(data.ms.Lookups/1000)))
				case "Mallocs":
					Graphicslist[i].Value = append(Graphicslist[i].Value, strconv.Itoa(int(data.ms.Mallocs)))
				case "Frees":
					Graphicslist[i].Value = append(Graphicslist[i].Value, strconv.Itoa(int(data.ms.Frees/1000)))
				case "HeapAlloc":
					Graphicslist[i].Value = append(Graphicslist[i].Value, strconv.Itoa(int(data.ms.HeapAlloc/1000)))
				case "StackInuse":
					Graphicslist[i].Value = append(Graphicslist[i].Value, strconv.Itoa(int(data.ms.StackInuse/1000)))
				case "StackSys":
					Graphicslist[i].Value = append(Graphicslist[i].Value, strconv.Itoa(int(data.ms.StackSys/1000)))
				case "OtherSys":
					Graphicslist[i].Value = append(Graphicslist[i].Value, strconv.Itoa(int(data.ms.OtherSys/1000)))
				case "NextGC":
					Graphicslist[i].Value = append(Graphicslist[i].Value, strconv.Itoa(int(data.ms.NextGC/1000)))
				case "PauseTotalNs":
					Graphicslist[i].Value = append(Graphicslist[i].Value, strconv.Itoa(int(data.ms.PauseTotalNs/1000)))
				case "NumForcedGC":
					Graphicslist[i].Value = append(Graphicslist[i].Value, strconv.Itoa(int(data.ms.NumForcedGC)))
				case "NumGC":
					Graphicslist[i].Value = append(Graphicslist[i].Value, strconv.Itoa(int(data.ms.NumGC)))
				}

			case "ring":
				switch Graphicslist[i].ID {
				case "Heap":
					Graphicslist[i].ID = "Heap"
					Graphicslist[i].Key = []string{"Idle", "Inuse", "Released", "Objects", "Sys"}
					Graphicslist[i].Value = make([]string, 5)
					Graphicslist[i].Value[0] = strconv.Itoa(int(data.ms.HeapIdle / 1000))
					Graphicslist[i].Value[1] = strconv.Itoa(int(data.ms.HeapInuse / 1000))
					Graphicslist[i].Value[2] = strconv.Itoa(int(data.ms.HeapReleased / 1000))
					Graphicslist[i].Value[3] = strconv.Itoa(int(data.ms.HeapObjects / 1000))
					Graphicslist[i].Value[3] = strconv.Itoa(int(data.ms.HeapSys / 1000))
				}
			}
			head = head.GetNext()
		}
		head = expvarinfo.GetHead()
	}
	temp.Execute(w, Graphicslist)
}

//StartMonitoringToHttp ..输入地址端口，开始web系统
func StartMonitoringToHttp(addr string, update time.Duration) {
	graphicslistInit()
	go func() {
		for {
			<-time.After(update)
			getExpvarInfo()
		}
	}()
	go func() {
		monserver := http.NewServeMux()
		monserver.HandleFunc("/debug", monitoringhandle)
		http.ListenAndServe(addr, monserver)
	}()
}

func graphicslistInit() {
	Graphicslist = append(Graphicslist, Graphics{
		Kind:  "colu",
		Name:  "内存",
		ID:    "Memory",
		Lable: "(M)",
	})
	Graphicslist = append(Graphicslist, Graphics{
		Kind:  "line",
		Name:  "堆空间",
		ID:    "Alloc",
		Lable: "(KB)",
	})
	Graphicslist = append(Graphicslist, Graphics{
		Kind:  "line",
		Name:  "堆空间累计",
		ID:    "TotalAlloc",
		Lable: "(KB)",
	})
	Graphicslist = append(Graphicslist, Graphics{
		Kind:  "line",
		Name:  "占用内存",
		ID:    "Sys",
		Lable: "(KB)",
	})
	Graphicslist = append(Graphicslist, Graphics{
		Kind:  "line",
		Name:  "runtime监视的指针数",
		ID:    "Lookups",
		Lable: "(K)",
	})
	Graphicslist = append(Graphicslist, Graphics{
		Kind:  "line",
		Name:  "malloc次数",
		ID:    "Mallocs",
		Lable: "(个)",
	})
	Graphicslist = append(Graphicslist, Graphics{
		Kind:  "line",
		Name:  "Frees回收",
		ID:    "Frees",
		Lable: "(KB)",
	})
	Graphicslist = append(Graphicslist, Graphics{
		Kind:  "line",
		Name:  "堆内存",
		ID:    "HeapAlloc",
		Lable: "(KB)",
	})
	obj := Graphics{
		Kind:  "ring",
		Name:  "Heap(KB)",
		ID:    "Heap",
		Key:   make([]string, 5),
		Value: make([]string, 5),
	}
	Graphicslist = append(Graphicslist, obj)
	Graphicslist = append(Graphicslist, Graphics{
		Kind:  "line",
		Name:  "栈空间",
		ID:    "StackInuse",
		Lable: "(KB)",
	})
	Graphicslist = append(Graphicslist, Graphics{
		Kind:  "line",
		Name:  "栈内存",
		ID:    "StackSys",
		Lable: "(KB)",
	})
	/*
		MSpanInuse uint64 //用于测试用的结构体使用的字节数
		MSpanSys uint64 //系统为测试用的结构体分配的字节数
		MCacheInuse uint64 //mcache结构体申请的字节数(不会被视为垃圾回收)
		MCacheSys uint64 //操作系统申请的堆空间用于mcache的字节数
		BuckHashSys uint64 //用于剖析桶散列表的堆空间
		GCSys uint64 //垃圾回收标记元信息使用的内存
		LastGC uint64 // 垃圾回收器最后一次执行时间。
		PauseNs [256]uint64 //一个循环队列，记录最近垃圾回收系统中断的时间
		PauseEnd [256]uint64 //一个循环队列，记录最近垃圾回收系统中断的时间开始点。
		BySize //内存分配器使用情况
	*/
	Graphicslist = append(Graphicslist, Graphics{
		Kind:  "line",
		Name:  "架构额外空间",
		ID:    "OtherSys",
		Lable: "(KB)",
	})
	Graphicslist = append(Graphicslist, Graphics{
		Kind:  "line",
		Name:  "GC监视",
		ID:    "NextGC",
		Lable: "(KB)",
	})
	Graphicslist = append(Graphicslist, Graphics{
		Kind:  "line",
		Name:  "服务中断(GC或getinfo)",
		ID:    "PauseTotalNs",
		Lable: "(次)",
	})
	Graphicslist = append(Graphicslist, Graphics{
		Kind:  "line",
		Name:  "强制GC",
		ID:    "NumGC",
		Lable: "(次)",
	})
	Graphicslist = append(Graphicslist, Graphics{
		Kind:  "line",
		Name:  "GC占比(CPU)",
		ID:    "NumForcedGC",
		Lable: `(%)`,
	})

}
